local skynet        = require "skynet"
local util          = require "util"
local constant      = require "constant"
local table_def     = require "table_def"
require "luaext"
local string_format = string.format

local table_unpack  = table.unpack
local table_insert  = table.insert
local table_remove  = table.remove
local math_random   = math.random
local math_floor    = math.floor
local math_ceil     = math.ceil
local MAX_SEAT_NUM                     = 6 --只能坐6个人
local utils         = require "utils"
local util          = require "util"
local nn_rule       = require "niuniu.nn_rule"

local M = {}

local Methods = {}
local MT = {
    __index = Methods
}

-- math.random(1,1000)
local function copy_table(t)
    local new_t = {}

    for k,v in pairs(t) do
        new_t[k] = v
    end

    return new_t
end

function M.new(uid, player_info, addr, fd,game_type,kpqz_conf, table_type)
  --  print("new_robot=============",tostring_r(player_info))
 --   print_r(player_info)
    local o = {
        uid                     = uid,
        player_info             = player_info,
        addr                    = addr,
        fd                      = fd,
        start_time              = util.get_now_time(),
        time_to_bet             = false,
        time_to_grab            = false,
        time_to_cacl_cattle     = false,
        bet_left_time           = 0,
        grab_left_time          = 0,        
        calc_cattle_left_time   = 0,
        bet_count               = 0,
        game_type               = game_type,
        
        life_duration           = math_random(10 * 60, 60 * 60), --机器人生命周期 10分钟~50分钟
        last_bet_time           = 0,
        leave                   = false,
        kpqz_conf               = kpqz_conf,
        record_last_player_bet  = 0,     --上一局真实玩家下注总额
        brnn_begin_bet_time     = false,        --百人牛牛开始下注的时间
        brnn_bet_time           = 7,     --百人牛牛机器人下注总时间(不能超过十秒)
        brnn_will_sit           = false,
        req_sit_time            = false,
        sit_position            = false,  --请求入座的位置
        coins_type              = coins_type,
        on_exiting              = false,
        --total_bet               = 0,
        side_bet                = {},
        --time_bet                = {},--时间区间下注额度,倒计时15~10秒为第一区间，倒计时10~5秒为第二区间，倒计时5~0秒为第三区间
        --stage_bet_coins         = {},
        bet_coins_list =        {},
        table_type = table_type,
    }

    return setmetatable(o, MT)
end

function Methods:on_register(uid)
    if uid ~= self.uid then
--        errlog(string_format("uid(%d) self.uid(%d) error", uid, self.uid))
        return false
    end

    local rob_info = {
        client_version  = '',
        channel         = '',
        cur_channel     = '',
        device_id       = '',
        device_brand    = '',
    }
    skynet.send(self.addr, "lua", "enter", self.uid, self.fd, rob_info)
end

function Methods:print(...)
    if self.table_type == 200100 then 
        errlog(...)
    end
end

--百人牛牛下注额度
function Methods:set_bet_coins(robot_bet_coins,robot_total_coins_type)
    if self.on_exiting then --正在退出
        return
    end

    if not robot_bet_coins then 
        return
    end

    local player_info = self.player_info
    if self.uid == self.instance:get_banker_uid() then --上庄机器人不投注
        return
    end

    local side_bet_coins = robot_bet_coins[player_info.coins_type]
    --这类机器人下注的总额度
    if not side_bet_coins or side_bet_coins == 0 then
        return 
    end    
    local robot_total_coins = (robot_total_coins_type[player_info.coins_type] == 0 or not robot_total_coins_type[player_info.coins_type]) and 1 or robot_total_coins_type[player_info.coins_type]
    dbglog("player_info.coins_type:",player_info.coins_type,player_info.type)
    local robot_control = _ENV.global_configs.robot[player_info.type]
    local robot_cfg = robot_control.robot_type_list[player_info.coins_type]
    dbglog("player_info.coins_type:",player_info.coins_type,player_info.type)
    local instance = self.instance
    local vip_seat_rate = robot_cfg.vip_seat_rate
    if math_random(1,100) <= vip_seat_rate then
        --请求上座
        self.brnn_will_sit = true
        self.req_sit_time = util.get_now_time() + math_random(1,20)          --随机一个时间入座
        self.sit_position = math_random(1,MAX_SEAT_NUM)
    end

    --确定各区域的下注上限额
    local bet_min           --机器人下注下限
    local last_bet_rate     --上局下注选用比例
    local bet_area_percent  --各下注区域比例

    --计算各个区域下注额度上限
    if instance.is_robot_banker then 
        bet_min = robot_cfg.system_bet_min
        bet_area_percent = utils.str_split(robot_cfg.system_bet_area,',')
        last_bet_rate = robot_cfg.system_last_bet_rate / 100
    else
        bet_min = robot_cfg.player_bet_min
        bet_area_percent = utils.str_split(robot_cfg.player_bet_area,',')
        last_bet_rate = robot_cfg.player_last_bet_rate / 100
    end

    self.bet_coins_list = {}
    local total_bet_coins = math_floor((self.player_info.coins or 0) / robot_total_coins * side_bet_coins)

    -- self:print("__1___total_bet_coins___", total_bet_coins, self.player_info.coins, robot_total_coins, side_bet_coins)
    local bet_weight = robot_cfg.bet_weight or {}
    local all_weight = 0
    local weight1 = bet_weight[1]
    local weight2 = bet_weight[2]
    if weight1 == weight2 and weight1 == 0 then 
        bet_weight[1] = 1
        bet_weight[2] = 1
    end
    for _,weight in pairs(bet_weight) do
        all_weight = all_weight + weight
    end

    local player_coins = self.player_info.coins
    local bet_coins = self.player_info.available_betting_list[1]

    local all_bet = 0

    local rand_list = {1,2,3,4}
    for _,weight in pairs(bet_weight) do
        local index = #self.player_info.available_betting_list --math_random(1, #self.player_info.available_betting_list) 
        local total_bet_coins_item = total_bet_coins*weight/all_weight
        local player_coins_item = player_coins*weight/all_weight
        local bet_coins_list_item = {}
        local side = table_remove(rand_list,math_random(1,#rand_list))
        --dbglog("side",side)
        bet_coins_list_item.side = side
        bet_coins_list_item.list = {}
        while true do
            local k = index
            if k < 1 then
                break
            end
            local tmp_bet_coins = self.player_info.available_betting_list[k]
            if (player_coins_item >= tmp_bet_coins) and (total_bet_coins_item >= tmp_bet_coins) then
                table_insert(bet_coins_list_item.list, tmp_bet_coins)
                all_bet = all_bet + tmp_bet_coins

                player_coins_item = player_coins_item - tmp_bet_coins
                total_bet_coins_item = total_bet_coins_item - tmp_bet_coins
            else
                index = index - 1
            end
        end
        table_insert(self.bet_coins_list, bet_coins_list_item)
    end

    for _,v in ipairs(self.bet_coins_list) do
        local count = 30 - #v.list
        for k = #v, count do
            table_insert(v.list, 0)
        end
        table.mix(v.list)
    end
    -- self:print("__2___total_bet_coins___", self.bet_coins_list)
    -- self:print("__3___total_bet_coins___", all_bet)
    -- self:print("__6___total_bet_coins___", player_info.available_betting_list)
    
    -- --算出各区域下注概率
    -- local side_bet = {}     --各下注区域下注概率
    -- -- for side = 1, 4 do
    -- --     side_bet[side] = tonumber(bet_area_percent[side] or 0)
    -- -- end

    -- --self.total_bet = bet_coins --个人下注总额
    -- self.side_bet = side_bet --个区域下注限制

    --print("brnn_tmp_total_coins========33",self.uid,tostring_r(self.time_bet))
end

function Methods:on_start(instance,grab_banker_max_times,robot_bet_coins,robot_total_coins_type)
  --  print("grab_banker_max_times",grab_banker_max_times)

    self:reset()
    self.instance = instance
    self.grab_banker_max_times = grab_banker_max_times
    self:set_bet_coins(robot_bet_coins,robot_total_coins_type)
end

function Methods:reset()
    self.instance               = nil
    self.bet_count              = 0 
    self.time_to_bet            = false
    self.time_to_grab           = false
    self.time_to_cacl_cattle    = false

    self.bet_left_time          = 0
    self.grab_left_time         = 0
    self.calc_cattle_left_time  = 0

    self.flottant               = 0.2 ----  -20%~20%
    self.leave = false
    
    self.record_last_player_bet = 0     --上一局真实玩家下注总额
    self.brnn_begin_bet_time    = false --百人牛牛开始下注的时间
    self.brnn_bet_time          = 7     --百人牛牛下注总时间
    self.brnn_will_sit          = false 
    self.req_sit_time           = false
    self.total_bet              = 0    --当局总下注额
    self.side_bet               = {}   --区域下注概率
    self.time_bet               = {}   --时间区域下注额
    self.bet_coins_list = {}
end

function Methods:close_robot()
    local coins_list        = self.player_info.available_betting_list
    local close             = true
    local player_info       = self.player_info
    local curr_coins        = player_info.coins

    for _, coins in pairs(coins_list) do
        if curr_coins >= coins then
            close = false
            break
        end
    end

    local curr_time = util.get_now_time()
--    print("br_close_robot===========",curr_time - self.start_time > self.life_duration)
    if curr_time - self.start_time > self.life_duration then
        close = true
    end

    return close
end

--机器人使用表情
function Methods:on_use_picture(uid,pic_conf,is_enter)
    if self.uid ~= uid then return end
    if not pic_conf then
        errlog(uid,"=====pic_conf is nil ===")
        return
    end

    local select_pic,delay_time
    if self.instance then
        select_pic,delay_time = self.instance:robot_can_use_pic(pic_conf)
    elseif is_enter then   --玩家刚进来时instance可能未初始化
        if math_random(1,100) <= pic_conf.use_rate then
            local use_pic = pic_conf.select_pic 
            select_pic = use_pic[math_random(1,#use_pic)]
            if pic_conf.delay_time and pic_conf.delay_time[1] and pic_conf.delay_time[2] then
                delay_time = math_random(pic_conf.delay_time[1],pic_conf.delay_time[2])
            end
        end 
    end
    if select_pic then
        if not delay_time or delay_time < 2 then 
            delay_time = math_random(2,6)     --至少给个2s种的延时吧
        end
        local msg = {
            type = 1,
            picture_id = select_pic,
        }
        if not self.picture_list then
            self.picture_list = {}
        end
        table_insert(self.picture_list,{
            send_time = util.get_now_time() + delay_time,    --每个动作之后，过两秒再发，避免动作和表情包同步，太假
            msg = msg
        }) 
        if self.instance then
            self.instance:add_use_pic_num()
        end
    end   
end

function Methods:judge_use_pic(curr_time)
    --上一次发表情的时间间隔小于1s，先不发，避免两个表情同步
    if self.last_pic_time and curr_time - self.last_pic_time < 1 then
        return
    end
    local picture_list = self.picture_list
    if picture_list then
        if #picture_list > 2 then
            table_remove(self.picture_list,1)
        end
        for k = #picture_list,1,-1 do
            local pic_info = picture_list[k]
            if curr_time > pic_info.send_time then
                skynet.send(self.addr,'lua','REQ_CHAT',self.uid,pic_info.msg,self.fd)
                table_remove(self.picture_list,k)
                return
            end
        end
    end
end

function Methods:on_game_over()
    local instance = self.instance

    if self.uid == self.instance:get_banker_uid() then --庄家机器人不能马上离开 需要先触发下庄
        return
    end
    self:canBet() 
    if self:close_robot() then
        skynet.send(self.addr, "lua", "REQ_LEAVE", self.uid, {}, self.fd)
        self.on_exiting = true
    end
    --机器人有一定概率的离桌率
    local robot_control = _ENV.global_configs.robot[self.player_info.type]
    local robot_cfg = robot_control.robot_type_list[self.player_info.coins_type]
    local leave_rate = robot_cfg.leave_rate or 5
    if math_random(1,100) <= leave_rate then
        skynet.send(self.addr, "lua", "REQ_LEAVE", self.uid, {}, self.fd)
        self.on_exiting = true
    end
    --self.picture_list = nil
    self.last_pic_time = nil
end

function Methods:judge_sit()
    local curr_time = util.get_now_time()
    if self.brnn_will_sit and curr_time > self.req_sit_time and self.sit_position and  not self.player_info.position then
        self.brnn_will_sit = false
        return true
    end
    return false
end

function Methods:judge_banker()
    local curr_time = util.get_now_time()
    if self.do_banker_time and curr_time > self.do_banker_time then
        self.do_banker_time = nil
        return true
    end

    return false
end

-- local function rand_side(side_bet)
-- --    print("==========",tostring_r(side_bet))
--     local side = 1
--     local sum = 0
--     for k,v in pairs(side_bet) do
--         sum = sum + v
--     end

--     if sum == 0 then
--         errlog(self.uid,"error side_bet is empty",tostring_r(side_bet))
--         return math_random(1,4)
--     end

--     local rand_num = math_random(1,sum)
--     local tmp_sum = 0
--     for side,v in pairs(side_bet) do
--         tmp_sum = tmp_sum + v
--         if rand_num <= tmp_sum then
--             return side
--         end
--     end

--     return side
-- end

function Methods:br_operate_logic()
    --self:judge_use_pic(util.get_now_time())
    if self.uid == self.instance:get_banker_uid() then --上庄机器人不投注
        return
    end
    if not self:getCanBet() then 
        return 
    end
    --判断是否到了上座的时间
    if self:judge_sit() then
        skynet.send(self.addr, "lua", "REQ_SITDOWN", self.uid, {position = self.sit_position}, self.fd)
    end

    if self:judge_banker() then
        skynet.send(self.addr, "lua", "REQ_DO_BANKER", self.uid, {}, self.fd)
    end

    local instance = self.instance
    if not instance:is_betting() then
        return 
    end

    local player_info = self.player_info
    local robot_control = _ENV.global_configs.robot[player_info.type]

    -- if util.get_now_time() <= self.last_bet_time + 1 then
    --     return
    -- end
-- self:print("__4___total_bet_coins___") 

    if not self.brnn_begin_bet_time or util.get_now_time() <= self.brnn_begin_bet_time then
        return
    end

    local curr_time = util.get_now_time()

    for _,v in pairs(self.bet_coins_list) do
        local side_coins = table_remove(v.list) or 0
        if side_coins <= 0 then
            goto continue
        end
-- self:print("__5___total_bet_coins___", side_coins) 
        --随机一个区域下注
        -- math.randomseed(util.get_now_time())
        local side = v.side
        --print("br_operate_logic===========side====222",side,self.time_bet[time_side])

        skynet.send(self.addr, "lua", "REQ_BET", self.uid, { side=side, bet_coins = side_coins}, self.fd)
        --self.stage_bet_coins[time_side] = (self.stage_bet_coins[time_side] or 0) - bet_coins
        self.last_bet_time = curr_time
        ::continue::
    end
    --print("br_operate_logic===========side====333",time_side,self.time_bet[time_side],bet_coins)
    --dbglog("==============333333rand_side",side)
    --dbglog("=============55555555555555555555total_bet",self.total_bet,instance.total_bet_coins,self.player_info.coins,bet_coins)
end

function Methods:on_time_to_grab(grab_left_time)
--    dbglog(string_format("grab_left_time(%d)", grab_left_time))
    self.grab_left_time = util.get_now_time() + math_random(3,4)
    self.time_to_grab = true
end

function Methods:on_time_to_bet(bet_left_time,record_last_player_bet)
    --dbglog(string_format("bet_left_time(%d)", bet_left_time))
    self.bet_left_time = util.get_now_time() + math_random(3,5)
    self.last_bet_time = util.get_now_time() + math_random(2,3)
    self.time_to_bet = true
    self.record_last_player_bet = record_last_player_bet      --上一局玩家下注总额
    self.brnn_begin_bet_time = util.get_now_time() -- + math_random(2,2)  --百人牛牛开始下注的时间
    self.brnn_bet_time = bet_left_time + 3                    --百人牛牛机器人下注总时间(不能超过十秒)
end

function Methods:on_time_to_cacl_cattle(calc_cattle_left_time)
 --   dbglog(string_format("calc_cattle_left_time(%d)", calc_cattle_left_time))
    self.calc_cattle_left_time = calc_cattle_left_time
    self.time_to_cacl_cattle = true
end

function Methods:on_do_banker(round)
    
    if self.last_do_banker_time then
        local robot_banker_control = _ENV.global_configs.robot_banker_control[self.player_info.type]
        if util.get_now_time() - self.last_do_banker_time < robot_banker_control.banker_interval then
            dbglog("can not do banker because interval",robot_banker_control.banker_interval)
            return
        end
    end

    self.total_round = round
  --  dbglog("on_do_bankerrrrrrrrrrrrrrrrr",round,self.uid,self.total_round,round)
    self.do_banker_time = util.get_now_time()
end

function Methods:on_become_banker()
    --机器人生命周期到了下庄
    local curr_time = util.get_now_time()
    if curr_time - self.start_time > self.life_duration then
        self.banker_round = 0
        self.total_round = 0
        self.last_do_banker_time = util.get_now_time()
        dbglog("on_become_banker life_duration",self.start_time,self.life_duration)
        skynet.send(self.addr, "lua", "REQ_UNDO_BANKER", self.uid, {}, self.fd)
        return
    end
  --  dbglog("on_become_bankerrrrrrrrrr",self.banker_round,"total_round",self.total_round,"uid",self.uid)
    self.banker_round = (self.banker_round or 0) + 1
    if self.banker_round and self.total_round and self.banker_round >= self.total_round then
        self.banker_round = 0
        self.total_round = 0
        self.last_do_banker_time = util.get_now_time()
        skynet.send(self.addr, "lua", "REQ_UNDO_BANKER", self.uid, {}, self.fd)
    end
end

--当有玩家申请上庄时，当局AI需随机在1-3局范围内结束并下庄,排队中的一名AI有一定几率取消排队
function Methods:on_player_do_banker()
 --   print("on_player_do_bankerrrrrrrrrrr")
    local robot_banker_control = _ENV.global_configs.robot_banker_control[self.player_info.type]
    local cancel_banker_rate = robot_banker_control.banker_cancel
 --   dbglog("cancel_banker_rate",cancel_banker_rate,self.uid,self.instance:get_banker_uid(),"self.total_round",self.total_round)
    if self.uid == self.instance:get_banker_uid() then
        local rand = math_random(1,3)
        if self.total_round and self.total_round - rand > self.banker_round then
            self.banker_round = self.total_round - rand
        end
    elseif math_random(1,100) <= cancel_banker_rate then
 --       dbglog("cancel_banker REQ_UNDO_BANKER")
        skynet.send(self.addr, "lua", "REQ_UNDO_BANKER", self.uid, {}, self.fd)
    end
end

local function get_first_four_cards(self)
    local side_cards_map = self.instance:get_side_cards_map()
    local player_data_map = self.instance:get_player_data_map()
    local side = player_data_map[self.uid].side
    local tmp_cards_list = side_cards_map[side]
    local self_card_list = {}

    for i = 1, 4 do 
        table_insert(self_card_list, tmp_cards_list[i])
    end

    return self_card_list
end

local function random_index(rob_prob_info)
    if not next(rob_prob_info) then
        return 0
    end

    local total = 0
    for _,value in pairs(rob_prob_info) do
        total = total + value
    end
    local rand = math_random(1,total)
    local curr = 0
    for index,value in ipairs(rob_prob_info) do
        curr = curr + value
        if rand <= curr then
            return index
        end
    end

    return 0
end

local function get_banker_times(self)
    local uid = self.uid
    local tile_list = get_first_four_cards(self)
    local card_value = nn_rule.get_cattle_four_number(tile_list)
    local kpqz_conf = self.kpqz_conf
    local rob_config = kpqz_conf.rob_config
    local banker_times = 0
    for _,rob_info in pairs(rob_config) do
        if card_value >= rob_info[1] and card_value <= rob_info[2] then
            local index = random_index(rob_info[3])
            banker_times = index - 1
            break
        end
    end

    return banker_times
end

local function get_bet_times(self)
    local uid = self.uid
    local tile_list = get_first_four_cards(self)
    local card_value = nn_rule.get_cattle_four_number(tile_list)
    local kpqz_conf = self.kpqz_conf
    local bet_config = kpqz_conf.bet_config
    local bet_times = 0
    for _,bet_info in pairs(bet_config) do
        if card_value >= bet_info[1] and card_value <= bet_info[2] then
            local index = random_index(bet_info[3])
            bet_times = index * 5
            break
        end
    end

    return bet_times
end

function Methods:update_play(curr_time)
    local instance = self.instance
    if not instance then
        return false
    end

    if self.on_exiting then --正在退出
        return
    end

    self:br_operate_logic()
end

function Methods:update()
    while not self.exit do
        local curr_time = util.get_now_time()
        self:judge_use_pic(curr_time)
        
        if self.instance then
            self:update_play(curr_time)
        end

        skynet.sleep(50)
    end
end

function Methods:on_exit()
    self.exit = true
    R().robotmgr(1):send('.msg_handler','put_back_robots',{{type=self.player_info.type,uid=self.uid,coins=self.player_info.coins}})
end


--是否可以下注
function Methods:canBet()
    --确定各区域的下注上限额
    local robot_control = _ENV.global_configs.robot[self.player_info.type]
    local robot_cfg = robot_control.robot_type_list[self.player_info.coins_type]     
    --判断可下注的概率
    local rate = robot_cfg.bet_rate or 100
    local rand_num = math_random(1, 100)
    -- print("___canBet_rate____",rand_num, rate)
    if rand_num > rate then 
        self.can_bet = false
        return 
    end
    self.can_bet = true
end

function Methods:getCanBet()
    return self.can_bet
end




return M
